#include "display.h"

/* LCD size */
#define LCD_H_RES (240)
#define LCD_V_RES (320)

/* LCD settings */
#define LCD_SPI_NUM (SPI2_HOST)
#define LCD_PIXEL_CLK_HZ (40 * 1000 * 1000)
#define LCD_CMD_BITS (8)
#define LCD_PARAM_BITS (8)
#define LCD_BITS_PER_PIXEL (16)
#define LCD_DRAW_BUFF_DOUBLE (1)
#define LCD_DRAW_BUFF_HEIGHT (50)
#define LCD_BL_ON_LEVEL (1)

/* LCD pins */
#define LCD_GPIO_SCLK (GPIO_NUM_47)
#define LCD_GPIO_MOSI (GPIO_NUM_48)
#define LCD_GPIO_MISO (GPIO_NUM_18)
#define LCD_GPIO_RST (GPIO_NUM_16)
#define LCD_GPIO_DC (GPIO_NUM_45)
#define LCD_GPIO_CS (GPIO_NUM_21)
#define LCD_GPIO_BL (GPIO_NUM_40)

static const char *TAG = "EXAMPLE";

/* LCD IO and panel */
static esp_lcd_panel_io_handle_t lcd_io = NULL;
static esp_lcd_panel_handle_t lcd_panel = NULL;
static lv_obj_t *tts_label = NULL;
static lv_obj_t *llm_label = NULL;
/* LVGL display and touch */
static lv_display_t *lvgl_disp = NULL;

typedef struct
{
    char *txt;
    char *emoji;
} emoji_t;

emoji_t emoji_list[] = {
    {"neutral", "😶"},
    {"happy", "🙂"},
    {"laughing", "😆"},
    {"funny", "😂"},
    {"sad", "😔"},
    {"angry", "😠"},
    {"crying", "😭"},
    {"loving", "😍"},
    {"embarrassed", "😳"},
    {"surprised", "😯"},
    {"shocked", "😱"},
    {"thinking", "🤔"},
    {"winking", "😉"},
    {"cool", "😎"},
    {"relaxed", "😌"},
    {"delicious", "🤤"},
    {"kissy", "😘"},
    {"confident", "😏"},
    {"sleepy", "😴"},
    {"silly", "😜"},
    {"confused", "🙄"}};

LV_FONT_DECLARE(font_puhui_14_1)
void display_lcd_init(void)
{

    gpio_config_t bk_gpio_config = {
        .mode = GPIO_MODE_OUTPUT,
        .pin_bit_mask = 1ULL << LCD_GPIO_BL};
    ESP_ERROR_CHECK(gpio_config(&bk_gpio_config));

    /* LCD initialization */
    ESP_LOGD(TAG, "Initialize SPI bus");
    const spi_bus_config_t buscfg = {
        .sclk_io_num = LCD_GPIO_SCLK,
        .mosi_io_num = LCD_GPIO_MOSI,
        .miso_io_num = GPIO_NUM_NC,
        .quadwp_io_num = GPIO_NUM_NC,
        .quadhd_io_num = GPIO_NUM_NC,
        .max_transfer_sz = LCD_H_RES * LCD_DRAW_BUFF_HEIGHT * sizeof(uint16_t),
    };
    spi_bus_initialize(LCD_SPI_NUM, &buscfg, SPI_DMA_CH_AUTO);

    const esp_lcd_panel_io_spi_config_t io_config = {
        .dc_gpio_num = LCD_GPIO_DC,
        .cs_gpio_num = LCD_GPIO_CS,
        .pclk_hz = LCD_PIXEL_CLK_HZ,
        .lcd_cmd_bits = LCD_CMD_BITS,
        .lcd_param_bits = LCD_PARAM_BITS,
        .spi_mode = 0,
        .trans_queue_depth = 10,
    };
    esp_lcd_new_panel_io_spi((esp_lcd_spi_bus_handle_t)LCD_SPI_NUM, &io_config, &lcd_io);

    ESP_LOGD(TAG, "Install LCD driver");
    const esp_lcd_panel_dev_config_t panel_config = {
        .reset_gpio_num = LCD_GPIO_RST,
        // .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_BGR,
        .rgb_ele_order = LCD_RGB_ELEMENT_ORDER_RGB,
        .bits_per_pixel = LCD_BITS_PER_PIXEL,
    };

    esp_lcd_new_panel_st7789(lcd_io, &panel_config, &lcd_panel);
    esp_lcd_panel_reset(lcd_panel);
    esp_lcd_panel_init(lcd_panel);
    esp_lcd_panel_mirror(lcd_panel, true, true);
    esp_lcd_panel_disp_on_off(lcd_panel, true);

    /* LCD backlight on */
    ESP_ERROR_CHECK(gpio_set_level(LCD_GPIO_BL, LCD_BL_ON_LEVEL));
}

void display_lvgl_init(void)
{

    const lvgl_port_cfg_t lvgl_cfg = {
        .task_priority = 4,       /* LVGL task priority */
        .task_stack = 4096,       /* LVGL task stack size */
        .task_affinity = -1,      /* LVGL task pinned to core (-1 is no affinity) */
        .task_max_sleep_ms = 500, /* Maximum sleep in LVGL task */
        .timer_period_ms = 5      /* LVGL timer tick period in ms */
    };
    lvgl_port_init(&lvgl_cfg);

    /* Add LCD screen */
    ESP_LOGD(TAG, "Add LCD screen");
    const lvgl_port_display_cfg_t disp_cfg = {
        .io_handle = lcd_io,
        .panel_handle = lcd_panel,
        .buffer_size = LCD_H_RES * LCD_DRAW_BUFF_HEIGHT,
        // .double_buffer = LCD_DRAW_BUFF_DOUBLE,
        .hres = LCD_H_RES,
        .vres = LCD_V_RES,
        .monochrome = false,
        .color_format = LV_COLOR_FORMAT_RGB565,
        .rotation = {
            .swap_xy = false,
            .mirror_x = true,
            .mirror_y = true,
        },
        .flags = {
            .buff_dma = true, .swap_bytes = true,
            .buff_spiram = true, // 把缓冲放入到外部内存
        }};
    lvgl_disp = lvgl_port_add_disp(&disp_cfg);

    lv_obj_t *scr = lv_scr_act();

    lvgl_port_lock(0);

    tts_label = lv_label_create(scr);
    lv_obj_set_width(tts_label, LCD_H_RES);
    lv_obj_set_style_text_align(tts_label, LV_TEXT_ALIGN_CENTER, 0);
    lv_obj_set_style_text_font(tts_label, &font_puhui_14_1, 0);
    lv_label_set_text(tts_label, "平安喜乐");
    lv_obj_align(tts_label, LV_ALIGN_CENTER, 0, 20);

    llm_label = lv_label_create(scr);
    lv_obj_set_width(llm_label, LCD_H_RES);
    lv_obj_set_style_text_align(llm_label, LV_TEXT_ALIGN_CENTER, 0);
    lv_obj_set_style_text_font(llm_label, font_emoji_64_init(), 0);
    lv_label_set_text(tts_label, "🤔");
    lv_obj_align(llm_label, LV_ALIGN_CENTER, 0, -60);

    lvgl_port_unlock();
}

void display_init(void)
{
    display_lcd_init();
    display_lvgl_init();
}

void display_show_tts(char *tts)
{
    lvgl_port_lock(0);
    lv_label_set_text(tts_label, tts);
    lvgl_port_unlock();
}

void display_show_llm(char *emotion)
{
    char *txt = "🙂";
    for (size_t i = 0; i < sizeof(emoji_list); i++)
    {
        if (strcmp(emotion, emoji_list[i].txt) == 0)
        {
            txt = emoji_list[i].emoji;
            break;
        }
    }

    lvgl_port_lock(0);
    lv_label_set_text(llm_label, txt); // 标签显示内容
    lvgl_port_unlock();
}

lv_obj_t *qr;
void display_show_qr(char *txt)
{
    if (qr)
        return;
    lvgl_port_lock(0);
    lv_color_t bg_color = lv_color_white();
    lv_color_t fg_color = lv_color_black();

    qr = lv_qrcode_create(lv_screen_active());
    lv_qrcode_set_size(qr, 200);
    lv_qrcode_set_dark_color(qr, fg_color);
    lv_qrcode_set_light_color(qr, bg_color);

    /*Set data*/
    const char *data = txt;
    lv_qrcode_update(qr, data, strlen(data));
    lv_obj_center(qr);

    /*Add a border with bg_color*/
    lv_obj_set_style_border_color(qr, fg_color, 0);
    lv_obj_set_style_border_width(qr, 2, 0);
    lvgl_port_unlock();
}

void display_del_qr(void)
{
    if (qr)
    {
        lvgl_port_lock(0);
        lv_obj_del(qr);
        lvgl_port_unlock();
        qr = NULL;
    }
}
